package cn.lingyangwl.framework.validation;

import cn.lingyangwl.framework.tool.core.exception.ArgumentNotValidException;
import cn.hutool.core.collection.CollUtil;
import lombok.extern.slf4j.Slf4j;
import org.hibernate.validator.HibernateValidator;

import javax.validation.ConstraintViolation;
import javax.validation.ConstraintViolationException;
import javax.validation.Validation;
import javax.validation.Validator;
import java.util.Set;

/**
 * 参数校验工具类
 *
 * @author shenguangyang
 */
@Slf4j
public class ValidationUtils {
    // 快速失败：只要有1个参数校验不通过，就立即停止校验参数
    private static final Validator VALIDATOR_FAST = Validation.byProvider(HibernateValidator.class).configure()
            .failFast(true).buildValidatorFactory().getValidator();
    private static final Validator VALIDATOR_ALL = Validation.byProvider(HibernateValidator.class).configure()
            .failFast(false).buildValidatorFactory().getValidator();

    /**
     * 校验遇到第一个不合法的字段直接返回不合法字段，后续字段不再校验
     *
     * @param domain 实体类
     * @return 校验结果
     */
    public static <T> Set<ConstraintViolation<T>> validateFast(T domain) {
        Set<ConstraintViolation<T>> validateResult = VALIDATOR_FAST.validate(domain);
        if (validateResult.size() > 0) {
            ConstraintViolation<T> next = validateResult.iterator().next();
            log.error("{}: {}", next.getPropertyPath(), next.getMessage());
        }
        return validateResult;
    }

    /**
     * 校验遇到第一个不合法的字段直接返回不合法字段，后续字段不再校验
     *
     * @param domain 实体类
     * @throws ArgumentNotValidException 参数无效异常
     */
    public static <T> void validateFastThrow(T domain) {
        Set<ConstraintViolation<T>> validateResult = VALIDATOR_FAST.validate(domain);
        if (validateResult.size() > 0) {
            ConstraintViolation<T> next = validateResult.iterator().next();
            throw new ArgumentNotValidException("{}: {}", next.getPropertyPath(), next.getMessage());
        }
    }

    /**
     * 校验所有字段并返回不合法字段
     *
     * @param domain 实体类
     * @return 校验结果
     */
    public static <T> Set<ConstraintViolation<T>> validateAll(T domain) {
        Set<ConstraintViolation<T>> validateResult = VALIDATOR_ALL.validate(domain);
        StringBuilder sb = new StringBuilder();
        if (validateResult.size() > 0) {
            for (ConstraintViolation<T> cv : validateResult) {
                sb.append(cv.getPropertyPath()).append(": ").append(cv.getMessage());
            }
            log.error(sb.toString());
        }
        return validateResult;
    }

    /**
     * 校验所有字段并返回不合法字段
     *
     * @param domain 实体类
     * @throws ArgumentNotValidException 参数无效异常
     */
    public static <T> void validateAllThrow(T domain) {
        Set<ConstraintViolation<T>> validateResult = VALIDATOR_ALL.validate(domain);
        StringBuilder sb = new StringBuilder();
        if (validateResult.size() > 0) {
            for (ConstraintViolation<T> cv : validateResult) {
                sb.append(cv.getPropertyPath()).append(": ").append(cv.getMessage());
            }
            throw new ArgumentNotValidException("{}", sb.toString());
        }
    }

    public static void validate(Validator validator, Object object, Class<?>... groups) {
        Set<ConstraintViolation<Object>> constraintViolations = validator.validate(object, groups);
        if (CollUtil.isNotEmpty(constraintViolations)) {
            throw new ConstraintViolationException(constraintViolations);
        }
    }
}
